/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pop3proxy.server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import pop3proxy.persistence.Mailbox;

/**
 *
 * @author zero.one
 */
public class TCPServer extends Thread {

    public static final int SERVER_PORT = 11000;
    ServerSocket welcomeSocket;  // TCP-Server-Socketklasse
    Socket connectionSocket;     // TCP-Standard-Socketklasse
    Mailbox mailbox;
    int counter = 0; // Zählt die erzeugten Bearbeitungs-Threads

    public TCPServer(Mailbox mailbox) {
        this.mailbox = mailbox;
    }

    public void run() {
        try {
            /* Server-Socket erzeugen */
            welcomeSocket = new ServerSocket(SERVER_PORT);

            while (true) { // Server laufen IMMER
                //System.out.println("Warte auf Verbindungswunsch auf Port "
                //        + SERVER_PORT);
                /*
                 * Blockiert auf Verbindungsanfrage warten --> nach
                 * Verbindungsaufbau Standard-Socket erzeugen und
                 * connectionSocket zuweisen
                 */
                connectionSocket = welcomeSocket.accept();
                /* Neuen Arbeits-Thread erzeugen und den Socket übergeben */
                (new TCPServerThread(++counter, connectionSocket, mailbox)).start();
            }
        } catch (IOException e) {
            System.err.println(e.toString());
        }

    }
}


/* Arbeitsthread, der eine existierende Socket-Verbindung zur Bearbeitung erhält */
class TCPServerThread extends Thread {

    private int name;
    private Socket socket;
    private Mailbox mailbox;
    private HashMap<String, List<String>> messageMap = new HashMap<String, List<String>>();//username -> all his messages
    DataOutputStream outToClient = null;
    List<String> emailList;
    
    public TCPServerThread(int num, Socket sock, Mailbox mailbox) {
        /* Konstruktor */
        this.name = num;
        this.socket = sock;
        this.mailbox = mailbox;
        emailList = mailbox.listMails();
    }

    public void run() {
        BufferedReader inFromClient;


        boolean isLoggedIn = false;
        String clientSentence;
        String messFromClient;//message vom client
        String client = " ";//enthält Username
        String password = " "; //enthält passwort
        String[] clientMArr;
        boolean serviceRequested = true; // Arbeitsthread beenden?
        AccountManager accMan = new AccountManager();//lista aller accounts
        int angegebenesListElem = 0;
        //List<String> listeFuerDELE = messageMap.get(client.toLowerCase());
        List<Integer> deleElems = new ArrayList<Integer>();

        //TESTDATEN
        Account a1 = new Account("david@test.de", "pass");
        accMan.addAccount(a1);
        accMan.addAccount(new Account("eyo", "pass"));
        List<String> testList = new ArrayList<String>();
        testList.add("Message1");
        testList.add("Message2");
        messageMap.put("david", testList);
        //TESTDATEN

//        System.out.println("TCP Server Thread " + name
//                + " is running until QUIT is received!");

        try {
            /* Socket-Basisstreams durch spezielle Streams filtern */
            inFromClient = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            outToClient = new DataOutputStream(socket.getOutputStream());
            this.messageToClient("+OK");

            while (serviceRequested) {
                /* String vom Client empfangen 
                 * HIER passiert die Logik------------------------------------------------------------*/
                clientSentence = inFromClient.readLine();
//                System.out.println("TCP Server Thread detected job: "
//                        + clientSentence);
                messFromClient = clientSentence;
                String command = clientSentence.split(" ")[0];
                
                if (command.length() < 4) {
                    this.messageToClient("-ERR Befehl zu kurz");
                    continue;
                }

                if (!isLoggedIn) {
                    if (command.equalsIgnoreCase("USER")) {
                        if (messFromClient.split(" ").length == 2) {
                            clientMArr = messFromClient.split(" ");
                            client = clientMArr[1];//der username
                            //this.messageToClient("Password for User " + client + " needed.");
                            this.messageToClient("+OK");
//                            System.out.println(client);//TEST
                        } else {
                            this.messageToClient("-ERR");
                        }

                    } else if (command.equalsIgnoreCase("PASS")) {
                        if (messFromClient.split(" ").length == 2) {
                            clientMArr = messFromClient.split(" ");
                            password = clientMArr[1];
                        } else {
                            this.messageToClient("-ERR");
                        }

//                        System.out.println(password);//TEST

                        List<Account> accList = accMan.getAccList();
//                        System.out.println(accMan.toString());//TEST

                        for (Account account : accList) {
                            if (account.equals(new Account(client.toLowerCase(), password))) {
                                isLoggedIn = true;
                                break;
                            } else {
                                isLoggedIn = false;
                            }
                        }
                        if (isLoggedIn) {
                            this.messageToClient("+OK");
                        } else {
                            this.messageToClient("-ERR " + "Anmeldung fuer " + client + " fehlgeschlagen");
                        }
//                        System.out.println(isLoggedIn);//TEST
                        //listeFuerDELE = messageMap.get(client);//zum rücksetzten der Liste für DELE
                    } else if (command.equalsIgnoreCase("CAPA")) {
                        this.messageToClient("+OK");
                        this.messageToClient("USER");
                        this.messageToClient(".");
                    } else if (!command.equalsIgnoreCase("QUIT")) {
                        this.messageToClient("-ERR");
                    }
//                    
                } else if (isLoggedIn && (command.equalsIgnoreCase("USER") || command.equalsIgnoreCase("PASS"))) {
                    this.messageToClient("-ERR - Es ist bereits ein anderer Account eingeloggt");
                } else if (isLoggedIn) {
                    if (command.equalsIgnoreCase("STAT")) {
                        long erg = 0;
                        for (String s : emailList) {
                            erg = erg + mailbox.mailSize(s);
                        }
                        this.messageToClient("+OK " + mailbox.size() + " " + erg);
                    } else if (command.equalsIgnoreCase("LIST")) {
                        if (messFromClient.split(" ").length == 2) {
                            String[] strArr = messFromClient.split(" ");
                            String temp = strArr[1];
                            angegebenesListElem = Integer.parseInt(temp);
                            if (angegebenesListElem > emailList.size()) {
                                this.messageToClient("-ERR - Sie haben weniger als " + angegebenesListElem + " Emails. Bitte geben Sie einen korrekten Index an");
                            } else {
                                this.messageToClient("+OK");
                                this.messageToClient(emailList.get(angegebenesListElem - 1));
                            }
                        } else {
                            long erg = 0;
                            this.messageToClient("+OK");
                            for (int c = 0; c < emailList.size(); ++c) {
                                if (!deleElems.contains(c))
                                    this.messageToClient((c+1) + " " + mailbox.mailSize(emailList.get(c)));
                            }
                            this.messageToClient(".");
                        }

                    } else if (command.equalsIgnoreCase("RETR")) {
                        if (messFromClient.split(" ").length == 2) {
                            String[] strArr = messFromClient.split(" ");
                            String temp = strArr[1];
                            angegebenesListElem = Integer.parseInt(temp);
                            if (angegebenesListElem > emailList.size()) {
                                this.messageToClient("-ERR - Sie haben weniger als " + angegebenesListElem + " Emails. Bitte geben Sie einen korrekten Index an");
                            } else {
                                this.messageToClient("+OK");
                                this.messageToClient(mailbox.readMail(emailList.get(angegebenesListElem - 1)));
                                this.messageToClient(".");
                            }
                        } else {
                            this.messageToClient("-ERR");
                        }
                    } else if (command.equalsIgnoreCase("DELE")) {
                        if (messFromClient.split(" ").length == 2) {
                            String[] strArr = messFromClient.split(" ");
                            String temp = strArr[1];
                            angegebenesListElem = Integer.parseInt(temp);
                            if (angegebenesListElem > emailList.size()) {
                                this.messageToClient("-ERR - Sie haben weniger als " + angegebenesListElem + " Emails. Bitte geben Sie einen korrekten Index an");
                            } else {
//                                List<String> replaceList = messageMap.get(client.toLowerCase());
//                                replaceList.remove(angegebenesListElem-1);
//                                messageMap.put(client.toLowerCase(), replaceList);
                                deleElems.add(angegebenesListElem - 1);
                                this.messageToClient("+OK");
                            }
                        } else {
                            this.messageToClient("-ERR");
                        }
                    } else if (command.equalsIgnoreCase("RSET")) {
                        //messageMap.put(client.toLowerCase(), listeFuerDELE);
                        String out = "Das/Die Element(e) * ";
                        for (Integer i : deleElems) {
                            out = out + (i + 1) + " ";
                        }
                        out = out + "* werden nun nicht mehr gelöscht";
                        this.messageToClient(out);
                        deleElems.clear();
                        this.messageToClient("+OK");
                    } else if (command.equalsIgnoreCase("UIDL")) {
                        if (messFromClient.split(" ").length == 2) {
                            String[] strArr = messFromClient.split(" ");
                            String temp = strArr[1];
                            angegebenesListElem = Integer.parseInt(temp);
                            if (angegebenesListElem > emailList.size()) {
                                this.messageToClient("-ERR - Sie haben weniger als " + angegebenesListElem + " Emails. Bitte geben Sie einen korrekten Index an");
                            } else {
                                String temp1 = strArr[1];
                                this.messageToClient(temp1);
                                this.messageToClient("+OK" + temp1 +" " + emailList.get(angegebenesListElem));
                            }
                        } 
                        else if(messFromClient.split(" ").length == 1){
                            this.messageToClient("+OK");
                            for(int i = 0; i < emailList.size(); ++i){
                                if (!deleElems.contains(i))
                                    this.messageToClient((i+1) + " " + emailList.get(i));
                            }
                            this.messageToClient(".");
                        }
                        else {
                            this.messageToClient("-ERR");
                        }
                    } else if (!command.equalsIgnoreCase("QUIT")) {
                        this.messageToClient("-ERR unbekanntes Kommando");
                    }
                } 
//                        else if (!isLoggedIn && (messFromClient.indexOf("UIDL") > -1 || messFromClient.indexOf("RSET") > -1
//                        || messFromClient.indexOf("DELE") > -1 || messFromClient.indexOf("RETR") > -1
//                        || messFromClient.indexOf("LIST") > -1 || messFromClient.indexOf("STAT") > -1)) {
//                    this.messageToClient("Niemand ist eingeloggt");
//                } else {
//                }

                
//                if(messFromClient.indexOf("AUTH") > -1){
//                    this.messageToClient("-ERR");
//                }

                /* Test, ob Arbeitsthread beendet werden soll */
                if (command.equalsIgnoreCase("QUIT")) {
                    serviceRequested = false;
                    if (deleElems != null && deleElems.size() > 0) {
                        //elems löschen
                        for (int i : deleElems) {
                            mailbox.deleteMail(emailList.get(i));
                        }
                    }
                    this.messageToClient("+OK");
                }
            }
            /* Socket-Streams schließen --> Verbindungsabbau */
            socket.close();
        } catch (IOException e) {
            System.err.println(e.toString());
            System.exit(1);
        }

//        System.out.println("TCP Server Thread " + name + " stopped!");
    }

    public void messageToClient(String message) throws IOException {
        outToClient.writeBytes(message + "\r\n");
        System.out.println("Sent:>>" + message);
    }

    public void intToClient(int number) throws IOException {
        String temp = String.valueOf(number);
        outToClient.writeBytes(temp + "\r\n");
    }
}
